package com.errol.generic.baiducloud.faceai.pojo.faceliveness;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.renjia.service.generic.baiducloud.basic.pojo.AbstractBaiduApiResultDTO;
import lombok.Getter;

import java.util.List;

/**
 * @author zhang xiao bin
 * @version v1.0
 * @date 2021/03/24
 * @since v
 **/
public class BaiduFaceVerifyResultDTO extends AbstractBaiduApiResultDTO {
    /**
     * 接口执行状态
     */
    private ResultStatus resultStatus;
    /**
     * resultStatus==SUCCESS 接口请求成功时有值，否则为null
     */
    private Result result;
    /**
     * 接口返回原始值，可能是对象，可能是列表
     */
    @JsonProperty(value = "result")
    private Object raw;

    public BaiduFaceVerifyResultDTO() {
    }

    public BaiduFaceVerifyResultDTO(ResultStatus resultStatus) {
        this.resultStatus = resultStatus;
    }

    public ResultStatus getResultStatus() {
        if (resultStatus == null) {
            resultStatus = ResultStatus.resolve(getErrCode());
        }
        return resultStatus;
    }

    public Result getResult() throws Exception {
        if (getResultStatus() == ResultStatus.SUCCESS) {
            if (result == null) {
                ObjectMapper objectMapper = new ObjectMapper().disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
                result = objectMapper.readValue(objectMapper.writeValueAsString(raw), Result.class);
            }
            return result;
        } else {
            return null;
        }
    }

    public Object getRaw() {
        return raw;
    }

    @Getter
    public static class Result {
        /**
         * 活体检测的总体打分 范围[0,1]，分数越高则活体的概率越大
         */
        private Float score;
        /**
         * 判断是否是合成图功能 范围[0,1]，分数越高则概率越大
         */
        @JsonProperty(value = "maxspoofing")
        private Float maxSpoofing;
        /**
         * 唇语识别结果 pass代表唇语验证通过，fail代表唇语验证未通过，当存在请求字段lip_identify字段值为 COMMON 或 STRICT时返回
         */
        @JsonProperty(value = "lip_language")
        private String lipLanguage;
        /**
         * 动作识别结果 pass代表动作验证通过，fail代表动作验证未通过，当存在请求字段type_identify字段值为action时返回
         */
        @JsonProperty(value = "action_verify")
        private String actionVerify;
        /**
         * 最佳图片信息
         */
        @JsonProperty(value = "best_image")
        private Image bestImage;
        /**
         * 返回1-8张抽取出来的图片信息
         */
        @JsonProperty(value = "pic_list")
        private List<Image> picList;
        /**
         * 验证码信息
         */
        private Code code;
        /**
         * 阈值 按活体检测分数>阈值来判定活体检测是否通过(阈值视产品需求选择其中一个)
         */
        private Threshold thresholds;

        /**
         * 判断是否是活体
         *
         * @return true 是活体
         */
        @JsonIgnore
        public boolean isFaceLive() {
            return score > thresholds.frr1e3;
        }

        /**
         * 判断语音验证码朗读是否正确
         *
         * @return true 正确
         */
        @JsonIgnore
        public boolean isLivenessCodeCorrect() {
            return code.similarity > 0.75;
        }

        /**
         * 判断是否时合成图攻击
         *
         * @return true 表示是合成图
         */
        @JsonIgnore
        public boolean isSpoofing() {
            return maxSpoofing > 0.00048;
        }
    }

    @Getter
    public static class Code {
        /**
         * 生成的验证码
         */
        private String create;
        /**
         * 验证码的语音识别结果
         */
        private String identify;
        /**
         * 验证码相似度 取值0~1 1代表完全一致 0代表完全不一致 推荐阈值0.75
         */
        private Float similarity;
    }

    @Getter
    public static class Image {
        /**
         * 人脸图片的唯一标识
         */
        @JsonProperty(value = "face_token")
        private String faceToken;
        /**
         * base64编码后的图片信息
         */
        private String pic;
        /**
         * 此图片的活体分数，范围[0,1]
         */
        @JsonProperty(value = "liveness_score")
        private Float livenessScore;
        /**
         * 判断此图片是合成图的分数，范围[0,1]
         */
        private Float spoofing;
    }

    @Getter
    public static class Threshold {
        @JsonProperty(value = "frr_1e-4")
        private Float frr1e4;
        @JsonProperty(value = "frr_1e-3")
        private Float frr1e3;
        @JsonProperty(value = "frr_1e-2")
        private Float frr1e2;
    }

    @Getter
    public enum ResultStatus {

        /**
         * 视频活体检测返回失败码、描述和建议，除 SUCCESS 外可以直接返回 hint 提示
         */
        SUCCESS(0, "成功", "success", "success"),
        UNKNOWN(-1, "未知错误", "unknown", "未知错误，请联系开发人员"),
        ERROR_FILE_TOO_BIG(-2, "视频文件太大", "video too big", "视频过大，请缩短时长重新录制"),
        ERROR_RUNTIME_EXCEPTION(-3, "视频活体验证执行异常", "face video verify error", "视频验证异常"),
        ERROR_216430(216430, "rtse/face 服务异常", "rtse/face service error", "服务调用失败，请稍后重试"),
        ERROR_216431(216431, "语音识别服务异常", "voice service error", "服务调用失败，请稍后重试"),
        ERROR_216432(216432, "视频解析服务调用失败", "video service call fail", "服务调用失败，请稍后重试"),
        ERROR_216433(216433, "视频解析服务发生错误", "video service error", "服务调用失败，请稍后重试"),
        ERROR_216434(216434, "活体检测失败", "liveness check fail", "服务调用失败，请稍后重试"),
        ERROR_216500(216500, "验证码位数错误", "code digit error", "验证码错误，请增加一层验证环节"),
        ERROR_216501(216501, "没有找到人脸", "not found face", "未检测到人脸信息，请按要求重新录制视频"),
        ERROR_216502(216502, "当前会话已失效", "session lapse", "语音验证码已过期，请重新录制视频"),
        ERROR_216505(216505, "redis连接失败", "redis connect error", "服务调用失败，请稍后重试"),
        ERROR_216506(216506, "redis操作失败", "redis operation error", "服务调用失败，请稍后重试"),
        ERROR_216507(216507, "视频中有多张人脸", "found many faces", "检测到多张人脸，请按要求重新录制视频"),
        ERROR_216508(216508, "没有找到视频信息", "not found video info", "视频格式错误"),
        ERROR_216509(216509, "视频中的声音无法识别", "voice can not identify", "声音无法识别（声音过低或有杂音），请按要求重新录制视频"),
        ERROR_216908(216908, "视频中人脸质量过低，具体原因看 error_msg", "此种类型具体错误原因看 error_msg", "视频中人脸质量过低，请按要求重新录制视频"),
        ERROR_222027(222027, "验证码长度错误(最小值大于最大值)", "code length param error", "验证码长度错误"),
        ERROR_222028(222028, "min_code_length 参数格式错误", "param[min_code_length] format error", "参数格式错误"),
        ERROR_222029(222029, "max_code_length 参数格式错误", "param[max_code_length] format error", "参数格式错误"),
        ERROR_222030(222030, "match_threshold 参数格式错误", "param[match_threshold] format error", "参数格式错误");

        private final Integer code;
        private final String msg;
        private final String desc;
        private final String hint;

        ResultStatus(Integer code, String desc, String msg, String hint) {
            this.code = code;
            this.desc = desc;
            this.msg = msg;
            this.hint = hint;
        }

        public static ResultStatus resolve(Integer code) {
            for (ResultStatus value : ResultStatus.values()) {
                if (value.getCode().equals(code)) {
                    return value;
                }
            }
            return UNKNOWN;
        }
    }
}
